﻿using System;
using System.Collections.Generic;
using System.Text;
using HtmlAgilityPack;
using System.Drawing;
using System.Net;
using System.IO;

namespace AMDb
{
    /// <summary>
    /// A class that represent the infromation
    /// </summary>
    public class Basic
    {
        /// <summary>
        /// Get the title.
        /// </summary>
        public string Title { get; internal set; }
        /// <summary>
        /// Get the release year.
        /// </summary>
        public string Year { get; internal set; }
        /// <summary>
        /// Get the poster.
        /// </summary>
        public Image Poster { get; internal set; }
        /// <summary>
        /// Get the meter rank.
        /// </summary>
        public string MeterRank { get; internal set; }
        /// <summary>
        /// Get the popularity (up or down).
        /// </summary>
        public string Popularity { get; internal set; }
        /// <summary>
        /// Get the difference of current meter rank with previous week.
        /// </summary>
        public string MeterChange { get; internal set; }
        /// <summary>
        /// Get the content rating.
        /// </summary>
        public string ContentRating { get; internal set; }
        /// <summary>
        /// Get the duration time (in minutes).
        /// </summary>
        public string Duration { get; internal set; }
        /// <summary>
        /// Get the genre(s).
        /// </summary>
        public string Genre { get; internal set; }
        /// <summary>
        /// Get the release date.
        /// </summary>
        public string ReleaseDate { get; internal set; }
        /// <summary>
        /// Get the votes rating
        /// </summary>
        public string Rating { get; internal set; }
        /// <summary>
        /// Get the number of votes.
        /// </summary>
        public string Votes { get; internal set; }
        /// <summary>
        /// Get the number of reviews from users.
        /// </summary>
        public string UserReviews { get; internal set; }
        /// <summary>
        /// Get the number of reviews from critics.
        /// </summary>
        public string CriticReviews { get; internal set; }
        /// <summary>
        /// Get the score from Metacritic.com.
        /// </summary>
        public string Metascore { get; internal set; }
        /// <summary>
        /// Get the number of reviews that metascore was based on.
        /// </summary>
        public string Metacritic { get; internal set; }
        /// <summary>
        /// Get the description.
        /// </summary>
        public string Description { get; internal set; }
        /// <summary>
        /// Get the director(s).
        /// </summary>
        public string Director { get; internal set; }
        /// <summary>
        /// Get the writer(s).
        /// </summary>
        public string Writers { get; internal set; }
        /// <summary>
        /// Get the star(s).
        /// </summary>
        public string Stars { get; internal set; }
        /// <summary>
        /// Get the awards.
        /// </summary>
        public string Awards { get; internal set; }
    }

    /// <summary>
    /// A class that gets the information of a movie.
    /// </summary>
    public class MovieInfo
    {
        private HtmlDocument doc = new HtmlDocument();
        private HtmlWeb web = new HtmlWeb();
        //Overview table
        private HtmlNode overview;
        /// <summary>
        /// Get or set the url
        /// </summary>
        public string Url;

        private Basic m;
        /// <summary>
        /// Initialize a new instance of the AMDb.IMDb.MovieInfo class to a specified Url.
        /// </summary>
        /// <param name="Url">The IMDb url</param>
        public MovieInfo(string Url)
        {
            this.Url = Url;
        }
        /// <summary>
        /// 
        /// </summary>
        public MovieInfo() { }
        
        /// <summary>
        /// Get the information
        /// </summary>
        /// <returns>Movie class</returns>
        public Basic getMovieInfo()
        {
            //Load the html of the page
            doc = web.Load(Url);
            //Load a part of html
            overview = doc.DocumentNode.SelectSingleNode("//div[@id='title-overview-widget']");
            m = new Basic();

            //Get the title
            m.Title = SSN(".//h1/span");
            //Get the year
            m.Year = SSN(".//h1/span[2]","B");
            //Get the poster
            m.Poster = URLtoImage(SSNA(".//div[@class='image']/a/img","src"));
            //Get the meter rank
            m.MeterRank = SSN(".//a[@id='meterRank']");
            //Get the popularity (up or down)
            m.Popularity = SSN(".//div[@id='meterChangeRow']/span");
            //Get the difference of current meter rank with previous week
            m.MeterChange = SSN(".//div[@id='meterChangeRow']/span[2]");
            //Get the content rating
            m.ContentRating = SSNA(".//span[@class='contentRating']", "title");
            //Get the duration
            m.Duration = SSN(".//time[@itemprop='duration']");
            //Get the release date
            m.ReleaseDate = SSN(".//a[@title='See all release dates']","N");
            //Get the description
            m.Description = SSN(".//p[@itemprop='description']");
            //Get the genre(s)
            var NodeCollection = SN(".//span[@itemprop='genre']");
            if(NodeCollection != null)
            {
                foreach (var genre in NodeCollection)
                    m.Genre += genre.InnerText + ", ";
                m.Genre = CLC(m.Genre);
            }
            else
                m.Genre = null;
            //Get the director(s)
            NodeCollection = SN(".//div[@itemprop='director']//span");
            if(NodeCollection != null)
            {
                foreach (var director in NodeCollection)
                    m.Director += director.InnerText + ", ";
                m.Director = CLC(m.Director);
            }
            else
                m.Director = null;
            //Get the writer(s)
            NodeCollection = SN(".//div[@itemprop='creator']//span");
            if(NodeCollection != null)
            {
                foreach (var writer in NodeCollection)
                    m.Writers += writer.InnerText + ", ";
                m.Writers = CLC(m.Writers);
            }
            else
                m.Writers = null;
            //Get the star(s)
            NodeCollection = SN(".//div[@itemprop='actors']//span[@itemprop='name']");
            if(NodeCollection != null)
            {
                foreach (var star in NodeCollection)
                    m.Stars += star.InnerText + ", ";
                m.Stars = CLC(m.Stars);
            }
            else
                m.Stars = null;
            //Get the votes rating
            m.Rating = SSN(".//div[@class='titlePageSprite star-box-giga-star']")+"/10";
            //Get the number of votes
            m.Votes = SSN(".//span[@itemprop='ratingCount']");
            //Get the number of reviews from users
            m.UserReviews = SSN(".//a[@href='reviews?ref_=tt_ov_rt']");
            //Get the number of reviews from critics
            m.CriticReviews = SSN(".//a[@href='externalreviews?ref_=tt_ov_rt']");
            //Get the score from Metacritic.com
            m.Metascore = SSN(".//a[@href='criticreviews?ref_=tt_ov_rt']");
            //Get the number of reviews that metascore was based on.
            m.Metacritic = SSN(".//a[@href='criticreviews?ref_=tt_ov_rt'][2]");
            //Get the awards
            NodeCollection = doc.DocumentNode.SelectNodes
                (".//div[@id='titleAwardsRanks']/a | .//div[@id='titleAwardsRanks']//span[@itemprop='awards']");
            if(NodeCollection != null)
            {
                foreach (var award in NodeCollection)
                {
                    if (award.InnerText != "")
                        m.Awards += award.InnerText + ", ";
                }
                m.Awards = CLC(m.Awards.Remove(m.Awards.Length -1));
            }
            else
                m.Awards = null;

            return m;
        }

        //Select single node
        private string SSN(string XPath)
        {
            HtmlNode temp = overview.SelectSingleNode(XPath);
            if (temp != null)
                return temp.InnerText.Trim();
            else
                return null;
        }
        //Select Nodes
        private HtmlNodeCollection SN(string XPath)
        {
            HtmlNodeCollection temp = overview.SelectNodes(XPath);
            if (temp != null)
                return temp;
            else
                return null;
        }
        //Select single node and apply a trim (TrimN or TrimB)
        private string SSN(string XPath, string Trim)
        {
            HtmlNode temp = overview.SelectSingleNode(XPath);
            if (temp == null)
                return null;
            else if (Trim == "N")
                return TrimN(temp.InnerText);
            else if (Trim == "B")
                return TrimB(temp.InnerText);
            else
                return temp.InnerText;
        }
        //Select attribute of a single node
        private string SSNA(string XPath, string Attribute)
        {
            HtmlNode temp = overview.SelectSingleNode(XPath);
            if (temp != null)
                return temp.Attributes[Attribute].Value;
            else
                return null;
        }
        //Clear last comma
        private string CLC(string input)
        {
            return input.Remove(input.Length - 2);
        }
        //Trim new lines
        private string TrimN(string input)
        {
            return input.Replace("\n", " ").Trim();
        }
        //Trim brackets
        private string TrimB(string input)
        {
            return input.Replace("(", "").Replace(")", "").Trim();
        }
        //Convert url to image
        private Image URLtoImage(string url)
        {
            using (WebClient wc = new WebClient())
            {
                if (url == null)
                    return null;
                byte[] bytes = wc.DownloadData(url);
                MemoryStream ms = new MemoryStream(bytes);
                return Image.FromStream(ms);
            }
        }
    }
}
